package commRedisService

import (
	"gitee.com/fierce_wolf/go-fox-edge-common/commEntity"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Differ"
	"time"
)

func (e *RedisService) IsInited() bool {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	return e.inited
}

func (e *RedisService) SetInited() {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	e.inited = true
}

func (e *RedisService) Init(entityList []commEntity.IEntity) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	e.dataMap = make(map[string]commEntity.IEntity)
	for _, entity := range entityList {
		e.dataMap[entity.MakeServiceKey()] = entity
	}

	// 初始化阶段，填写记录更新更新状态和结构有效时间戳
	updateTime := time.Now().UnixMilli()
	e.agileMap = make(map[string]int64)
	for _, entity := range entityList {
		e.agileMap[entity.MakeServiceKey()] = updateTime
	}

	e.needSave = true
}

func (e *RedisService) copyEntityMap() (map[string]commEntity.IEntity, error) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	dataMap := make(map[string]commEntity.IEntity)
	for k, v := range e.dataMap {
		dataMap[k] = v
	}

	return dataMap, nil
}

func (e *RedisService) copyEntityList() []commEntity.IEntity {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	list := make([]commEntity.IEntity, 0)
	for _, v := range e.dataMap {
		list = append(list, v)
	}

	return list
}

func (e *RedisService) foreachEntityList(finder func(entity commEntity.IEntity) bool) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	for _, v := range e.dataMap {
		finder(v)
	}
}

func (e *RedisService) findEntityList(finder func(entity commEntity.IEntity) bool) []commEntity.IEntity {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	list := make([]commEntity.IEntity, 0)
	for _, v := range e.dataMap {
		if finder(v) {
			list = append(list, v)
		}
	}

	return list
}

func (e *RedisService) findEntity(finder func(entity commEntity.IEntity) bool) commEntity.IEntity {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	for _, v := range e.dataMap {
		if finder(v) {
			return v
		}
	}

	return nil
}

func (e *RedisService) getEntityCount(finder func(entity commEntity.IEntity) bool) int {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	count := 0
	for _, v := range e.dataMap {
		if finder(v) {
			count++
		}
	}

	return count
}

func (e *RedisService) getEntityById(id int64) commEntity.IEntity {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	for _, v := range e.dataMap {
		if id == v.GetBaseEntity().Id {
			return v
		}
	}

	return nil
}

func (e *RedisService) setEntityByKey(key string, entity commEntity.IEntity) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	e.dataMap[key] = entity
}

func (e *RedisService) getEntityByKey(key string) commEntity.IEntity {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	return e.dataMap[key]
}

func (e *RedisService) getEntityByKeys(keys []string) map[string]commEntity.IEntity {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	result := make(map[string]commEntity.IEntity)
	for _, key := range keys {
		entity := e.dataMap[key]
		if entity != nil {
			result[key] = entity
		}
	}

	return result
}

func (e *RedisService) setDataMap(dataMap map[string]commEntity.IEntity) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	e.dataMap = dataMap
}

func (e *RedisService) delEntityByKey(key string) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	delete(e.dataMap, key)
}

func (e *RedisService) setAgileMap(agileMap map[string]int64) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	e.agileMap = agileMap
}

func (e *RedisService) getAgileMapLen() int {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	return len(e.agileMap)
}

func (e *RedisService) setAgileByKey(key string, agile int64) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	e.agileMap[key] = agile
}

func (e *RedisService) delAgileByKey(key string) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	delete(e.agileMap, key)
}

func (e *RedisService) differSaveAgile(oldUpdateTimes map[string]int64) (map[string]int64, map[string]int64) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	addList := make(map[string]int64)
	delList := make(map[string]int64)
	eqlList := make(map[string]int64)
	Differ.Map.KStringVInt64(oldUpdateTimes, e.agileMap, addList, delList, eqlList)

	return addList, delList
}

func (e *RedisService) compareLoadAgile(newUpdateTimes map[string]int64) (map[string]int64, map[string]int64, map[string]int64) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	addList := make(map[string]int64)
	delList := make(map[string]int64)
	eqlList := make(map[string]int64)

	Differ.Map.KStringVInt64(e.agileMap, newUpdateTimes, addList, delList, eqlList)
	diff := compareAgile(e.agileMap, newUpdateTimes)

	return diff, addList, delList
}

func (e *RedisService) compareSaveAgile(oldUpdateTimes map[string]int64) map[string]int64 {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	return compareAgile(e.agileMap, oldUpdateTimes)
}

func compareAgile(newDatas map[string]int64, oldDatas map[string]int64) map[string]int64 {
	diff := make(map[string]int64)
	if len(newDatas) != len(oldDatas) {
		return diff
	}

	for k, v := range newDatas {
		newValue := v
		oldValue := oldDatas[k]

		if newValue != oldValue {
			diff[k] = v
		}
	}

	return diff
}

func (e *RedisService) getUpdateTime() int64 {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	return e.updateTime
}

func (e *RedisService) setUpdateTime(updateTime int64) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	e.updateTime = updateTime
}

func (e *RedisService) setNeedSave(needSave bool) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	e.needSave = needSave
}

func (e *RedisService) isNeedSave() bool {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	return e.needSave
}

func (e *RedisService) SetTypeNotify(notify ConsumerTypeNotify) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	e.typeNotify = notify
}

func (e *RedisService) getTypeNotify() *ConsumerTypeNotify {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	return &e.typeNotify
}

func (e *RedisService) getEntityNotify() []ConsumerEntityNotify {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	list := make([]ConsumerEntityNotify, 0)
	for _, k := range e.entityNotify {
		list = append(list, k)
	}

	return list
}

func (e *RedisService) buildDataMapToJson() map[string]interface{} {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	writeMap := make(map[string]interface{})
	// 将[string:entity]转换为[string:string]格式
	for k, entity := range e.dataMap {
		po, err := entity.BuildToJson()
		if err != nil {
			continue
		}

		writeMap[k] = po
	}

	return writeMap
}

func (e *RedisService) buildAgileMapToJson() map[string]interface{} {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	writeMap := make(map[string]interface{})
	for k, v := range e.agileMap {
		writeMap[k] = v
	}

	return writeMap
}

func (e *RedisService) getDataMapValue(key string) commEntity.IEntity {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	return e.dataMap[key]
}

func (e *RedisService) getNotifyTypeData(addList map[string]int64, delList map[string]int64, diff map[string]int64) (int64, map[string]commEntity.IEntity, map[string]commEntity.IEntity, map[string]commEntity.IEntity) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	if e.typeNotify == nil {
		return 0, nil, nil, nil
	}

	// 保存具体数据：删除部分的数据，在从redis读取之前，存在于缓存中，此时要提前取出
	addMap := make(map[string]commEntity.IEntity)
	mdyMap := make(map[string]commEntity.IEntity)
	delSet := make(map[string]commEntity.IEntity)
	for key := range addList {
		val,ok := e.dataMap[key]
		if ok {
			addMap[key] = val
		}
	}
	for key := range delList {
		delSet[key] = nil
	}
	for key := range diff {
		val,ok := e.dataMap[key]
		if ok {
			mdyMap[key] = val
		}
	}

	// 检查：是否有变更的数据
	if len(addMap) == 0 && len(delList) == 0 && len(mdyMap) == 0 {
		return 0, nil, nil, nil
	}

	return e.updateTime, addMap, delSet, mdyMap
}

func (e *RedisService) notifyType(addList map[string]int64, delList map[string]int64, difList map[string]int64) {
	if e.typeNotify == nil {
		return
	}

	updateTime, addMap, delSet, mdyMap := e.getNotifyTypeData(addList, delList, difList)
	e.typeNotify.Notify(updateTime, addMap, delSet, mdyMap)
}

func (e *RedisService) notifyEntity(addList map[string]int64, delList map[string]int64, difList map[string]int64) {
	entityNotify := e.getEntityNotify()

	for i := 0; i < len(entityNotify); i++ {
		notify := entityNotify[i]

		key := notify.getServiceKey()
		entity := e.getDataMapValue(key)

		_, ok := addList[key]
		if ok {
			notify.notifyInsert(entity)
		}
		_, ok = difList[key]
		if ok {
			notify.notifyUpdate(entity)
		}
		_, ok = delList[key]
		if ok {
			notify.notifyDelete(key)
		}
	}
}
